package com.hb.mybatis.interceptor;

import com.hb.annotation.FieldEncrypt;
import com.hb.encryptor.IEncryptor;
import com.hb.util.EncryptedFieldsProvider;
import com.hb.util.InterceptorUtil;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;

/**
 * @author hubin
 * @description
 * @createTime 2024/3/10 17:10
 */
@Component
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class MybatisDecryptionPlugin implements Interceptor, ApplicationListener<ContextRefreshedEvent> {

    private static Logger logger = LoggerFactory.getLogger(MybatisDecryptionPlugin.class);

    @Autowired
    private IEncryptor encryptor;

    @Autowired
    private List<SqlSessionFactory> sqlSessionFactories;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        decryptObj(result);
        return result;
    }

    private void decryptObj(Object obj) throws Exception {
        if (obj == null) {
            return;
        }
        if (obj instanceof Collection) {
            Collection<?> list = (Collection<?>) obj;
            if (list.isEmpty()) {
                return;
            }
            Object firstNonNullItem = list.stream().filter(Objects::nonNull).findFirst().orElse(null);
            if (!InterceptorUtil.decryptionRequired(firstNonNullItem)) {
                return;
            }
            Set<Field> encryptedFields = EncryptedFieldsProvider.get(firstNonNullItem.getClass());
            if (encryptedFields == null || encryptedFields.isEmpty()) {
                return;
            }
            for (Object item : list) {
                decryptObj(item);
            }
        } else {
            if (InterceptorUtil.decryptionRequired(obj)) {
                Set<Field> encryptedFields = EncryptedFieldsProvider.get(obj.getClass());
                if (encryptedFields == null || encryptedFields.isEmpty()) {
                    return;
                }
                for (Field field : encryptedFields) {
                    decryptField(field, obj);
                }
            }
        }
    }

    private void decryptField(Field field, Object obj) throws Exception {
        FieldEncrypt encryptedField = field.getAnnotation(FieldEncrypt.class);
        if (encryptedField == null) {
            return;
        }
        Object cipher = field.get(obj);
        if (cipher == null || StringUtils.hasLength(cipher.toString())) {
            return;
        }
        // 加解密的 key 需要是16个字符
        String key = "1234567812345678";
        if (cipher instanceof String) {
            String plain = encryptor.decrypt(cipher.toString(), key);
            field.set(obj, plain);
        } else {
            decryptObj(cipher);
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        for (SqlSessionFactory factory : sqlSessionFactories) {
            factory.getConfiguration().addInterceptor(this);
        }
    }
}
